﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FinanSoft.Entities;
using FinanSoft.DataLayer;
using FinanSoft.Utilities;

namespace FinanSoft.BusinessLayer.BuySaleStrategy
{
    public class InvoiceDocManager : BuySaleDocManager
    {

        #region Atributos
        private bool _IsError;
        private String _ErrorMessage;
        #endregion

        #region Constructor
        public InvoiceDocManager()
        {
            _IsError = false;
            _ErrorMessage = "";
        }
        #endregion

        #region Propiedades
        /// <summary>
        /// Devuelve si hubo o no un error
        /// </summary>
        public bool IsError
        {
            get { return _IsError; }
            set { _IsError = value; }
        }

        /// <summary>
        /// Devuelve el mensaje de error
        /// </summary>
        public String ErrorMessage
        {
            get { return _ErrorMessage; }
            set { _ErrorMessage = value; }
        }

        #endregion

        #region Métodos
        /// <summary>
        /// Método que se encarga de guardar un documento de tipo entrada o entrega de mercancías
        /// </summary>
        /// <param name="document">Documento que se va a guardar</param>
        /// <param name="docType">Tipo de documento EM o EE...</param>
        /// <returns>Retorna si hubo o no éxito en la operación</returns>
        public bool SaveDocument(Entities.Document document, string docType)
        {
            // Se obtiene el tipo de documento
            DocType type = DocumentTypeManager.GetInstance().GetType(docType);
            // Obtenemos el documento base para la factura, si no hay va a ser nulo
            Document baseDoc = BuySaleManager.GetInstance().GetDocument(document.BaseDocId);
            // Verificamos que haya suficientes elementos para cada detalle
            if(docType.Equals(Constants.FC_ACCOUNTING_ENTRY) && (baseDoc == null || baseDoc.tipoDocumento.Equals(Constants.OV_ACCOUNTING_ENTRY)))
                foreach (DocDetail detail in document.detalleDoc)
                    if (detail.Item.GetStoreRoomInfo(detail.StoreRoom.Id).Stock < detail.Quantity)
                    {
                        _IsError = true;
                        _ErrorMessage = Constants.NO_ENOUGH_UNITS;
                        return false;
                    }
            // Se intenta guardar en bd el encabezado del documento primero para que nos devuelva el id
            int docId = BuySaleDataManager.GetInstance().SaveDocumentHeader(document, type);
            if (docId == Constants.INVALID_ID)
            {
                _IsError = true;
                _ErrorMessage = Constants.ERROR_SAVING_DOC;
                return false;
            }
            // Intentamos guardar los artículos para el documento que se insertó anteriormente
            foreach (DocDetail detail in document.detalleDoc)
            {
                string docCode = DocumentTypeManager.GetInstance().GetType(docType).CurrentNumber.ToString();
                AuxiliarAccount auxAccount = AuxiliaryAccountManager.GetInstance().GetAuxiliarAccount(document.IdCuentaAuxiliar);
                ItemStoreRoomInfo itemStoreInfo = detail.Item.GetStoreRoomInfo(detail.StoreRoom.Id);
                Currency docCurrency = MoneyManager.GetInstance().GetCurrency(document.IdMoneda);
                List<AccountingEntryDetail> invoiceDetails = new List<AccountingEntryDetail>();
                double localUnitCost = Math.Round(detail.UnitCost * docCurrency.ToLocalCurrency, 2);
                Constants.AccountingEntryType auxInc;
                Constants.AccountingEntryType otherInc;
                if (docType.Equals(Constants.FP_ACCOUNTING_ENTRY))
                {
                    auxInc = Constants.AccountingEntryType.credit;
                    otherInc = Constants.AccountingEntryType.debit;
                    if (baseDoc == null || baseDoc.tipoDocumento == Constants.OC_ACCOUNTING_ENTRY)
                    {
                        bool result = ItemManager.GetInstance().MakeInventoryInputMovement(detail.Item.Id, detail.StoreRoom.Id, localUnitCost, (int)Math.Round(detail.Quantity), auxAccount.Parent, auxAccount.AccountID, docType);
                        if (!result)
                        {
                            _ErrorMessage = ItemManager.GetInstance().ErrorMessage;
                            return false;
                        }
                        if (baseDoc != null)
                            itemStoreInfo.Solicitado -= (int)Math.Round(detail.Quantity);
                    }
                    else
                    {
                        int allocInventoryId = CatalogManager.GetInstance().GetAccountingCatalog().GetSpecialAccounts(Constants.SpecialType.allocationInventory)[0].AccountID;
                        double allocLocalAmount = Math.Round(detail.Quantity * detail.UnitCost * docCurrency.ToLocalCurrency, 2);
                        double allocSystemAmount = Math.Round(allocLocalAmount / MoneyManager.GetInstance().SystemCurrency.ToLocalCurrency);
                        invoiceDetails.Add(AccountingEntryManager.GetInstance().BuildAccountingEntryDetail(
                            allocInventoryId, allocSystemAmount, allocLocalAmount, Constants.AccountingEntryType.debit, Constants.INVALID_ID));
                        invoiceDetails.Add(AccountingEntryManager.GetInstance().BuildAccountingEntryDetail(
                            auxAccount.Parent, allocSystemAmount, allocLocalAmount, Constants.AccountingEntryType.credit, auxAccount.AccountID));
                    }
                }
                else
                {
                    auxInc = Constants.AccountingEntryType.debit;
                    otherInc = Constants.AccountingEntryType.credit;
                    if (baseDoc == null || baseDoc.tipoDocumento == Constants.OV_ACCOUNTING_ENTRY)
                    {
                        bool result = ItemManager.GetInstance().MakeInventoryOutputMovement(detail.Item.Id, detail.StoreRoom.Id, (int)Math.Round(detail.Quantity), itemStoreInfo.CuentaCostoVentas.AccountID, Constants.INVALID_ID, docType);
                        if (!result)
                        {
                            _ErrorMessage = ItemManager.GetInstance().ErrorMessage;
                            return false;
                        }
                        if (baseDoc != null)
                            itemStoreInfo.Comprometido -= (int)Math.Round(detail.Quantity);
                    }
                    double salesLocalAmount = Math.Round(detail.Quantity * detail.UnitCost * docCurrency.ToLocalCurrency, 2);
                    double salesSystemAmount = Math.Round(salesLocalAmount / MoneyManager.GetInstance().SystemCurrency.ToLocalCurrency, 2);
                    invoiceDetails.Add(AccountingEntryManager.GetInstance().BuildAccountingEntryDetail(
                        itemStoreInfo.CuentaVentas.AccountID, salesSystemAmount, salesLocalAmount, Constants.AccountingEntryType.credit, Constants.INVALID_ID));
                    invoiceDetails.Add(AccountingEntryManager.GetInstance().BuildAccountingEntryDetail(
                        auxAccount.Parent, salesSystemAmount, salesLocalAmount, Constants.AccountingEntryType.debit, auxAccount.AccountID));
                }
                // Hacemos el asiento para el impuesto de ventas
                double taxLocalAmount = Math.Round(detail.Quantity * detail.UnitCost * detail.Tax.Porcentaje * MoneyManager.GetInstance().GetCurrency(document.IdMoneda).ToLocalCurrency, 2);
                double taxSystemAmount = Math.Round(taxLocalAmount / MoneyManager.GetInstance().SystemCurrency.ToLocalCurrency, 2);
                int taxId = CatalogManager.GetInstance().GetAccountingCatalog().GetSpecialAccounts(Constants.SpecialType.salesTaxToPay)[0].AccountID;
                invoiceDetails.Add(AccountingEntryManager.GetInstance().BuildAccountingEntryDetail
                    (auxAccount.Parent, taxSystemAmount, taxLocalAmount, auxInc, auxAccount.AccountID));
                invoiceDetails.Add(AccountingEntryManager.GetInstance().BuildAccountingEntryDetail
                    (taxId, taxSystemAmount, taxLocalAmount, otherInc, Constants.INVALID_ID));
                bool entryResult = AccountingEntryManager.GetInstance().CreateAndRecordAccountingEntry(
                    docType, document.fechaVencimiento, document.fechaEmision, PeriodManager.GetInstance().GetCurrentAccountingPeriod().GetCurrentSeason().Id,
                    "Código "+ docCode, "", "", invoiceDetails);
                if (!entryResult)
                {
                    _IsError = true;
                    _ErrorMessage = AccountingEntryManager.GetInstance().ErrorMessage;
                    return false;
                }
                bool noErrorDB = BuySaleDataManager.GetInstance().SaveDocumentArticle(detail, docId);
                if (!noErrorDB)
                {
                    _IsError = true;
                    _ErrorMessage = Constants.ERROR_SAVING_DOCS_ARTICLES;
                    return false;
                }
            }
            DocumentTypeManager.GetInstance().IncreaseDocumentConsecutiveNumber(docType);
            return true;
        }

        public string GetErrorMessage()
        {
            return _ErrorMessage;
        }
        #endregion
    }
}
